﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Configuration;
using System.Collections;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.IO;
using ServerServices;
using PadiCalClient;
using System.Threading;
using CommonLib;

namespace PuppetMaster
{
    public partial class Form1 : Form
    {

        TcpChannel tcp;
        Process server1;
        Process server2;
        Process server3;

        private string userSelected;

        Hashtable serverList = new Hashtable();

        Hashtable clientsList = new Hashtable();

        private IDictionary<string, string> serverAddress = new Dictionary<string, string>();

        private IDictionary<string, string> clientAddress = new Dictionary<string,string>();

        IClientPuppet clientPuppet;

        IServerService serverOperations;


        //ArrayList clientsList = new ArrayList();

        public Form1()
        {
            InitializeComponent();

            //InitializeServers();

            tcp = new TcpChannel();

            ChannelServices.RegisterChannel(tcp, false);

        }

        private void InitializeServers()
        {
            server1 = new Process();
            server2 = new Process();
            server3 = new Process();

            server1.StartInfo.FileName = "C:\\Users\\Nuno\\Documents\\Visual Studio 2008\\Projects\\padical\\padical\\PadiCalServer\\bin\\Debug\\PadiCalServer.exe";
            server1.StartInfo.Arguments = "1 9999";
            server1.StartInfo.WindowStyle=ProcessWindowStyle.Minimized;

            server2.StartInfo.FileName = "C:\\Users\\Nuno\\Documents\\Visual Studio 2008\\Projects\\padical\\padical\\PadiCalServer\\bin\\Debug\\PadiCalServer.exe";
            server2.StartInfo.Arguments = "2 9998";
            server2.StartInfo.WindowStyle=ProcessWindowStyle.Minimized;

            server3.StartInfo.FileName = "C:\\Users\\Nuno\\Documents\\Visual Studio 2008\\Projects\\padical\\padical\\PadiCalServer\\bin\\Debug\\PadiCalServer.exe";
            server3.StartInfo.Arguments = "3 9997";
            server3.StartInfo.WindowStyle=ProcessWindowStyle.Minimized;

            server1.Start();
            server2.Start();
            server3.Start();
        }

        private void kill_procesess()
        {
            IDictionaryEnumerator en = serverList.GetEnumerator();
            Process a;

            while (en.MoveNext())
            {
                a = (Process)en.Value;
                a.Kill();
            }

            en = clientsList.GetEnumerator();
            while (en.MoveNext())
            {
                a = (Process)en.Value;
                a.Kill();
            }
        }

        /* Quando o programa encerra */
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            kill_procesess();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }


        private string OpenScript(){

            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter =
               "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            //dialog.InitialDirectory =
            dialog.Title = "Select a script";
            return (dialog.ShowDialog() == DialogResult.OK)
               ? dialog.FileName : null;
        }

        private void scriptButton_Click(object sender, EventArgs e)
        {
            string file = OpenScript();

            StreamReader tr = new StreamReader(file);
            string line;
            string[] elements;
            
            while (tr.Peek() >= 0)
            {
                line = tr.ReadLine();
                elements = line.Split(' ');
                /* Connect command */
                if (elements[0].Equals("connect"))
                {

                    string[] address = elements[2].Split(':');

                    if (elements[1].Equals("central-1") ||
                        elements[1].Equals("central-2") ||
                        elements[1].Equals("central-3"))
                    {
                        connectServer(elements[1], address[0], address[1]);
                    }
                    else
                    {
                        connectClient(elements[1], address[0], address[1]);
                    }
                    
                    /* readCalendar command */
                }else if(elements[0].Equals("readCalendar")){

                    setCalendar(elements[1]);

                    //readCalendar(elements[1]);

                /* disconnect command */
                }else if (elements[0].Equals("disconnect")){

                    if (elements[1].Equals("central-1") || elements[1].Equals("central-2") || elements[1].Equals("central-3"))
                    {
                        serverDisconnect(elements[1]);
                    }
                    else
                    {
                        clientDisconnect(elements[1]);
                    }
                /* reservation command */
                }else if (elements[0].Equals("reservation")){
                    int begin = line.IndexOf('{');
                    int end = line.IndexOf('}');
                    string reservationInfo = line.Substring(begin+1, end - begin-1);
                    string[] reservationElements = reservationInfo.Split(';');

                    string ReservationName = reservationElements[0].Trim();

                    ArrayList ReservationUsers = new ArrayList();
                    string[] users = reservationElements[1].Split(',');
                    foreach (string user in users)
                        ReservationUsers.Add(user.Trim());

                    ArrayList ReservationSlots = new ArrayList();
                    string[] slots = reservationElements[2].Split(',');
                    foreach (string slot in slots)
                        ReservationSlots.Add(slot.Trim());

                    doReservation(ReservationName, ReservationUsers, ReservationSlots);

                }
                else if (elements[0].Equals("wait"))
                {
                    int time = Int32.Parse(elements[1]) * 1000;
                    Thread.Sleep(time);
                }
                else if (elements[0].Equals("shutdown"))
                {
                    kill_procesess();
                }
            }

            tr.Close();
        }

        private void serverDisconnect(string name)
        {

            serverOperations = (IServerService)Activator.GetObject(typeof(IServerService),
            "tcp://" + serverAddress[name] + "/ServerService");

            Console.WriteLine("disconnected server:"+name);
            if (serverOperations.getThisServer().Status == 1)
            {
                serverOperations.putOffline();

              //  Console.WriteLine("verificar o estado do servidor disconnectado nos outros servers");
                List<PServer> listaServers = serverOperations.getListOfKnownServers();

                    List<PServer> list = new List<PServer>();

                    foreach (PServer aServer in listaServers)
                    {
                        IServerService servicos = (IServerService)Activator.GetObject(typeof(IServerService),
                   "tcp://" + aServer.Ip.ToString() + ":" + aServer.Port + "/ServerService");

                        //servicos.printKnowingServers();
                    }
              //  Console.WriteLine("Server "+serverOperations.getThisServer().Id+" Status:"+serverOperations.getThisServer().Status);
            }

        }

        private void CreateClient_Click(object sender, EventArgs e)
        {
            connectClient(UserText.Text, "localhost",PortText.Text);
        }

        private void ClientCombo_SelectedIndexChanged(object sender, EventArgs e)
        {
            String[] split = ClientCombo.Text.Split(' ');
            readCalendar(split[0]);
            userSelected = split[0];       
        }

        private void serverSelector_SelectedIndexChanged(object sender, EventArgs e)
        {
            /* Melhorar isto
             *  - Melhorar a escolha do server
             */

            String[] split = serverSelector.Text.Split(' ');
            
            serverOperations = (IServerService)Activator.GetObject(typeof(IServerService),
            "tcp://" + serverAddress[split[0]] + "/ServerService");
            
            IDictionary<string, User> test = serverOperations.getPeers();
            userList.Items.Clear();
            foreach (KeyValuePair<string, User> kvp in test)
            {
                userList.Items.Add("User = " + kvp.Key + "\tAddress = " + kvp.Value.Ip.ToString());
            }
        }


        private void connectServer(string name, string address, string port)
        {
            if(serverList.Contains(name))
            {
                serverOperations = (IServerService)Activator.GetObject(typeof(IServerService),
                "tcp://" + serverAddress[name] + "/ServerService");

                if (serverOperations.isConnected() == 0)
                {
                    serverOperations.putOnline();
                }

            }else{

                Process newServer = new Process();

                newServer.StartInfo.FileName = "C:\\Users\\Nuno\\Documents\\Visual Studio 2008\\Projects\\padical\\padical\\PadiCalServer\\bin\\Debug\\PadiCalServer.exe";
                newServer.StartInfo.Arguments = serverList.Count+1 + " " + address +" "+port;
                newServer.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;

                newServer.Start();

                serverList.Add(name, newServer);

                serverAddress.Add(name, address+":"+port);
                serverSelector.Items.Add(name+" "+address+":"+port+" Connected");

            }
        }

        private void connectClient(string name,string address, string port)
        {

            if (clientsList.ContainsKey(name))
            {
                clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                    "tcp://" + clientAddress[name] + "/ClientPuppet");

                if (!clientPuppet.isConnected())
                {
                    clientPuppet.connect(port);

                    string[] split = clientAddress[name].Split(':');
                    clientAddress[name] = address + ":" + port;
                    ClientCombo.Items.Remove(name + " " + address + ":" + split[1] + " Disconnected");
                    ClientCombo.Items.Add(name + " " + clientAddress[name] + " Connected");
                }
            }
            else
            {

                if (!address.Equals("127.0.0.1"))
                {
                    clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                    "tcp://" + address + ":" + port + "/ClientPuppet");

                    if (!clientPuppet.isConnected())
                    {
                        clientPuppet.connect(port);
                        clientAddress.Add(name, address + ":" + port);
                        ClientCombo.Items.Add(name + " " + address + ":" + port + " Connected");
                    }
                }
                else
                {
                    Process newClient = new Process();

                    newClient.StartInfo.FileName = "C:\\Users\\Nuno\\Documents\\Visual Studio 2008\\Projects\\padical\\padical\\PadiCalClient\\bin\\Debug\\PadiCalClient.exe";
                    newClient.StartInfo.Arguments = name + " " + port;

                    //TODO newClient.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;

                    newClient.Start();

                    clientsList.Add(name, newClient);
                    clientAddress.Add(name, address + ":" + port);

                    /*clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                        "tcp://" + address + ":" + port + "/ClientPuppet");

                    clientPuppet.connect(port);*/

                    ClientCombo.Items.Add(name + " " + address + ":" + port + " Connected");
                }
            }
        }

        private void readCalendar(string user)
        {
            if(clientsList.Contains(user)){
      
                clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                "tcp://" + clientAddress[user] + "/ClientPuppet");

                String[] list = clientPuppet.getCalendar();

                slotList.Items.Clear();
                for (int i = 0; i < list.Length; i++)
                
                    slotList.Items.Add(i + " " + list[i]);

            }
        }

        private void clientDisconnect(string name)
        {
            clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                "tcp://" + clientAddress[name] + "/ClientPuppet");

            clientPuppet.disconnect();
    
            ClientCombo.Items.Remove(name +" "+ clientAddress[name] + " Connected");
            ClientCombo.Items.Add(name + " " + clientAddress[name] + " Disconnected");

        }

        private void doReservation(string ReservationName, ArrayList ReservationUsers, ArrayList Reservationslots)
        {
            string user = (string)ReservationUsers[0];

            clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                "tcp://" + clientAddress[user] + "/ClientPuppet");

            InitReservation init = new InitReservation(ReservationName, ReservationUsers, Reservationslots);

            if (clientPuppet.isConnected())
            {
                clientPuppet.receivedInitReservation(init);
            }
        }

        private void setCalendar(string user)
        {
            if (clientsList.Contains(user))
            {
                clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                "tcp://" + clientAddress[user] + "/ClientPuppet");

                if (clientPuppet.isConnected())
                    ClientCombo.SelectedItem = user + " " + clientAddress[user] + " Connected";
                else
                    ClientCombo.Items.Add(user + " " + clientAddress[user] + " Disconnected");
            }
        }

        private void slotList_SelectedIndexChanged(object sender, EventArgs e)
        {
            reservationList.Items.Clear();

            clientPuppet = (IClientPuppet)Activator.GetObject(typeof(IClientPuppet),
                "tcp://" + clientAddress[userSelected] + "/ClientPuppet");

            ArrayList Reservations = clientPuppet.getSlotReservations(slotList.SelectedIndex);
            foreach (Reservation res in Reservations)
            {
                reservationList.Items.Add(res.Name + " " + res.State);
            }
        }
    }
}